
#include <stdlib.h>
#include <stdio.h>
#include <pthread.h>
#include <arpa/inet.h>
#include <sys/select.h>
#include <string.h>
#include <semaphore.h>
#include <time.h>
#include <sys/socket.h>
#include <stdint.h>
#include "hilosFunciones.h"

tipoConfigPRAID configPRAID;
int32_t offsetSectorMaximo;
int32_t nuevoDescriptor = 0, j = 0, maxDescriptor = 0, hayDisco = 0,
		cantSectores = 0, isockEscucha = 0, discosSincronizando = 0;
t_queue *auxConPedidos;
nodoDiscos *discosActivos;
nodoDiscos *siguienteDiscoAMandarPedido = NULL;
sem_t semaforoSincro;

void aceptarDisco(paquete *paqueteRecibido, nodoDiscos **losDiscos) {
	if (*losDiscos == NULL) {
		offsetSectorMaximo = handshakePRAID_PPDInicial(nuevoDescriptor, paqueteRecibido);
	} else {
		//TODO rechazar el disco si ya esta
		handshakePRAID_PPD(nuevoDescriptor, paqueteRecibido);
	}
	agregarDisco(losDiscos, nuevoDescriptor, paqueteRecibido->payload);
	discosActivos = *losDiscos;
	crearHiloDisco(losDiscos);
	hayDisco++;
}

void nuevaConexion(fd_set *misConexiones, paquete *paqueteRecibido, nodoDiscos **losDiscos) {

	if (strcmp(paqueteRecibido->payload, "")) {
		//se conecta PPD
		aceptarDisco(paqueteRecibido, losDiscos);
		if (configPRAID.consolaActivada)
			mensajes(paqueteRecibido->payload, nuevoPlanificador, NULL, NULL);
	} else {
		//se conecta filesystem
		uint32_t resultadoHS;
		resultadoHS = handshakePRAID_FS(nuevoDescriptor, paqueteRecibido,
				hayDisco);
		if (resultadoHS) {
			FD_SET(nuevoDescriptor, misConexiones);
			//meter a lista el nuevoDescriptor
			maxDescriptor = nuevoDescriptor;
		} else {
			close(nuevoDescriptor);
		}
	}
}

//No sinc - No Sinc - Sinc
//Disco 1 - Disco 2 - Disco 3

nodoDiscos *siguienteDiscoAPedir(){
	nodoDiscos *disco;
	disco = siguienteDiscoAMandarPedido;
	int encontrado = 1;
	while(encontrado){
		if(disco->sig!=NULL){
			disco = disco->sig;
			if(disco->estadoDisco==sincronizado)
				encontrado = 0;
		}
		else{
			disco = discosActivos;
			if(disco->estadoDisco==sincronizado)
				encontrado = 0;
		}
	}
	return disco;
}

void distribuirPedido(uint32_t socket, nodoDiscos **losDiscos, nodoDiscos **siguienteDiscoAMandarPedido) {
	//pasar pedido al o a los discos
	paquete *paqueteHS;
	paqueteHS = (paquete*)malloc(sizeof(paquete));
	pedido *ped = (pedido*)malloc(sizeof(pedido));
	int32_t resultado = RAID_RECIBEPEDIDO(socket, paqueteHS);
	if(resultado==-1){
		perror("Conexion: ");
		close(socket);
		return;
	}
	copiarDatosFS(paqueteHS, ped, socket);
	free(paqueteHS);
	if (ped->operacion == lectura) {
		collection_queue_push((*siguienteDiscoAMandarPedido)->pedidos, ped);
		sem_post(&(*siguienteDiscoAMandarPedido)->pedidos->semaforoPedidos);
		*siguienteDiscoAMandarPedido = siguienteDiscoAPedir();

		/*int encontrado = 1;
		  while (encontrado) {
			if ((*siguienteDiscoAMandarPedido)->sig == NULL)
				*siguienteDiscoAMandarPedido = *losDiscos;
			else {
				if ((*siguienteDiscoAMandarPedido)->sig->estadoDisco
						== sincronizado)
					*siguienteDiscoAMandarPedido
							= (*siguienteDiscoAMandarPedido)->sig;
				else
					*siguienteDiscoAMandarPedido = *losDiscos;
			}
			if ((*siguienteDiscoAMandarPedido)->estadoDisco == sincronizado)
				encontrado = 0;
		}*/

	} else {
		//es de escritura entonces se los paso a todos los discos
		if (discosSincronizando == 0) {
			/*no se está sincronizando ningún disco*/
			ponerPedidoEnDiscos(ped, losDiscos);
		} else {
			/*se esta sincronizando algun disco entonces lo guardo en una cola auxiliar*/
			collection_queue_push(auxConPedidos, ped);
		}

	}
}

int main(int argc, char **argv) {
	fd_set misConexiones;
	fd_set fd_sEsclavo;
	FD_ZERO(&misConexiones);
	sem_init(&semaforoSincro, 0, 100000);
	nodoDiscos *losDiscos = NULL;
	discosActivos = NULL;
	int32_t auxMaximoSocket;
	char *archivoConfig;
	archivoConfig = (char*) malloc(strlen(argv[1]) + 1);
	strcpy(archivoConfig, argv[1]);
	configPRAID = leerArchConfigPRAID(archivoConfig);
	isockEscucha = abre_SocketServidor(configPRAID.puerto, configPRAID.ip);
	FD_SET(isockEscucha, &misConexiones);
	maxDescriptor = auxMaximoSocket = isockEscucha;
	if (configPRAID.consolaActivada)
		mensajes(0, inicioRaid, NULL, NULL);

	while (1) {
		fd_sEsclavo = misConexiones;
		if (select(auxMaximoSocket + 1, &fd_sEsclavo, NULL, NULL, NULL) == -1) {
			printf("error función select");
			break;
		}
		for (j = isockEscucha; j <= auxMaximoSocket; j++) {//while lista vacia
			if (FD_ISSET(j, &fd_sEsclavo)) {
				if (j == isockEscucha) {
					nuevoDescriptor = acepta_Socket(isockEscucha);

					paquete *paqueteRecibido;
					paqueteRecibido = handshakePRAID(nuevoDescriptor);

					nuevaConexion(&misConexiones, paqueteRecibido, &losDiscos);
					break;
				} else {
					//es un pedido del filesystem
					distribuirPedido(j, &losDiscos, &siguienteDiscoAMandarPedido);
					break;
				}
			}
		}
		auxMaximoSocket = maxDescriptor;
	}
	return 1;
}
